#ifndef VALUE_H
#define VALUE_H

#include <string>
#include <map>
#include <algorithm>
#include <cctype>

#include "OgreMath.h"
#include "OgreVector2.h"
#include "OgreVector3.h"
#include "OgreVector4.h"
#include "OgreQuaternion.h"
#include "OgreColourValue.h"
#include "OgreAny.h"

#include "BulletDynamics/Dynamics/btRigidBody.h"

#include <boost/function.hpp>
#include <boost/variant.hpp>
#include <boost/any.hpp>

class Object;
class Function;

enum ValueType
{
//  POD
	NIL=0,
	INT,
	FLOAT,
	BOOL,
	STRING,

//
	OBJECT,
	ARRAY,
	FUNCTION,

//  Ogre
	VECTOR2,
	VECTOR3,
	VECTOR4,
	QUATERNION,
	COLOUR,
	RADIAN,
	DEGREE,

//  Bullet
	RIGIDBODY,

//
	UNKNOWN/*=0*/
};


//{{{ "Type-traits" templates
template<typename A, typename B>
struct is_same { enum { type = false }; };

template<typename A>
struct is_same<A, A> { enum { type = true }; };

template <typename T>
struct is_pointer
{ static const bool value = false; };

template <typename T>
struct is_pointer<T*>
{ static const bool value = true; };


//typedef void(*NullaryFnPtr)(void);
//struct NullaryFnObj { virtual void operator()(void) = 0; };

template<typename T>
struct type_of
{
	static const ValueType object =
	    // POD
	    is_same<T, bool>				::type ? BOOL 		:
	    is_same<T, int>					::type ? INT		:
	    is_same<T, float>				::type ? FLOAT 		:
	    is_same<T, Ogre::Real>			::type ? FLOAT 		:
	    is_same<T, btScalar>			::type ? FLOAT 		:
	    is_same<T, std::string>			::type ? STRING 	:
	    is_same<T, const char*>			::type ? STRING 	:

	    is_same<T, ::Object>			::type ? OBJECT		:
	    is_same<T, ::Function >			::type ? FUNCTION 	:
	    is_same<T, ::Function* >			::type ? FUNCTION 	:

	    // Ogre
	    is_same<T, Ogre::Vector2>		::type ? VECTOR3 	:
	    is_same<T, Ogre::Vector3>		::type ? VECTOR3 	:
	    is_same<T, Ogre::Vector4>		::type ? VECTOR4 	:
	    is_same<T, Ogre::Quaternion>	::type ? QUATERNION :
	    is_same<T, Ogre::ColourValue>	::type ? COLOUR 	:
	    is_same<T, Ogre::Radian>		::type ? RADIAN 	:
	    is_same<T, Ogre::Degree>		::type ? DEGREE 	:

	    // Bullet
	    is_same<T, btRigidBody>			::type ? RIGIDBODY	:

	    UNKNOWN;
};
//}}}



//!TODO: boost bi-directional map?
/**
 * \brief A simple class thats used in doing some type-checking
 */
class TypeInfo
{
public:
	typedef std::pair<std::string, ValueType> StringValueTypePair;
	typedef std::map<std::string, ValueType> StringValueTypeMap;

//	template<typename T>
//	static ValueType getType(T t) {
// if(is_same....
//		return UNKNOWN;
//	}

	/** \brief does a lower-case lookup **/
	static ValueType getType(std::string t)
	{
		if(!init) setup();

		std::transform(
		    t.begin(),
		    t.end(),
		    t.begin(),
		    (int(*)(int)) tolower);

		return types.find(t) == types.end() ? UNKNOWN : types[t];
	}

	template<typename T>
	static const char* getTypeName()
	{
		return
		    is_same<T, bool>				::type ? "bool" 		:
		    is_same<T, float>				::type ? "float" 		:
		    is_same<T, int>					::type ? "int" 			:
		    is_same<T, const char*>			::type ? "string" 		:
		    is_same<T, std::string>			::type ? "string" 		:

		    is_same<T, Ogre::Real>			::type ? "float" 		:
		    is_same<T, Ogre::String>	 	::type ? "string" 		:
		    is_same<T, Ogre::ColourValue>	::type ? "ColourValue" 	:
		    is_same<T, Ogre::Degree>		::type ? "Degree" 		:
		    is_same<T, Ogre::Radian>		::type ? "Radian" 		:
		    is_same<T, Ogre::Camera>		::type ? "Camera" 		:
		    is_same<T, Ogre::Vector2>		::type ? "Vector2" 		:
		    is_same<T, Ogre::Vector3>		::type ? "Vector3" 		:
		    is_same<T, Ogre::Vector4>		::type ? "Vector4" 		:
		    is_same<T, Ogre::Quaternion>	::type ? "Quaternion" 	:
		    is_same<T, Ogre::SceneNode>		::type ? "SceneNode" 	:
		    is_same<T, Ogre::Entity>		::type ? "Entity" 		:

		    is_same<T, btRigidBody>			::type ? "RigidBody" 	:

		    is_same<T, ::Object>			::type ? "Object" 		:
		    is_same<T, ::Function>			::type ? "Function" 	:
		    is_same<T, ::Function*>			::type ? "Function" 	:

		    "nil"
//		    typeid(T).name();
		    ;
	}

//    static std::string getTypeName(const ValueType& type) { return types.find(type).first; }
//    static void Register(const std::string& s, ValueType t) { types.insert(StringValueTypePair(s,t)); }

	static StringValueTypeMap types;

private:
	static void setup();
	static bool init;
};

//{{{ Value
class Value
{
public:
	static const Value null;
	static const Value NaN;

	Value(const Value& v);
	Value(ValueType _type = NIL) throw();
//	Value() 				 throw() : type(NIL) 	{ data.ptrData = 0;   }
	Value(bool v) 				 throw() : type(BOOL) 	{ data.boolData = v;  }
	Value(int v) 				 throw() : type(INT) 	{ data.intData = v;   }
	Value(float v) 				 throw() : type(FLOAT) 	{ data.floatData = v; }
	Value(const char* v) 		 throw() : type(STRING) { data.cstrData = v;  }
	Value(const std::string& v)  throw() : type(STRING) { data.cstrData = v.c_str(); }

	virtual ~Value() throw();

	Value& operator= (const Value& v);       //{ type = v.type; data = v.data; 	 return *this; }
	Value& operator= (bool v);               //{ type = BOOL; 	 data.boolData = v;  return *this; }
	Value& operator= (int v);                //{ type = INT; 	 data.intData = v; 	 return *this; }
	Value& operator= (float v);              //{ type = FLOAT;  data.floatData = v; return *this; }
//	Value& operator= (void* v)              { type = NIL; 	 data.ptrData = 0;   return *this; }

	bool operator==(const Value& v);	//{ return this == &v; }
	bool operator!=(const Value& v);	//{ return this != &v; }

	//!TODO: implement
	Value clone()   const { return Value::null; }
	void swap( Value &other );

	bool isNull()    const throw() { return type == NIL; }
	bool isInt()     const throw() { return type == INT; }
	bool isFloat()   const throw() { return type == FLOAT; }
	bool isString()  const throw() { return type == STRING; }
	bool isObject()  const throw() { return type == OBJECT; }
	bool isNumeric() const throw() { return type == INT || type == FLOAT; }
	
	template<typename U>
	U as() const throw()
	{
		switch(type_of<U>::object)
			{
			case INT:
				switch(type)
					{
					case NIL: return 0;
					case BOOL: return data.boolData ? 1 : 0;
					case INT: return data.intData;
					case FLOAT: return (int)data.floatData;
					default: return 0;
					}
				break;
			case FLOAT:
				switch(type)
					{
					case NIL: return 0.0;
					case BOOL: return data.boolData ? 1.0 : 0.0;
					case INT: return (float)data.intData;
					case FLOAT: return data.floatData;
					default: return 0.0;
					}
				break;
/*				
			case STRING:
				switch(type)
					{
					case NIL: return "";
					case STRING: return data.cstrData ? data.cstrData : "";
					case BOOL:	return data.boolData ? "true" : "false";
					default: return "";
					}
				break;
*/
			default:
				assert(0);
				break;
			}
	}

	friend std::ostream& operator<<(std::ostream& stream, Value v)
	{
		switch(v.type)
			{
			case NIL:
				return stream << "NULL";
				break;

			case INT:
				return stream << v.data.intData;
				break;
			case BOOL:
				return stream << v.data.boolData;
				break;
			case FLOAT:
				return stream << v.data.floatData;
				break;
			case STRING:
				return stream << v.data.cstrData;
				break;

			case OBJECT:
//				return stream << v.data.objData;
				break;

			default:
				return stream << "unknown";
				break;
			}
	}

protected:
	union ValueUnion
	{
		bool			boolData;
		int 			intData;
		float 			floatData;
		const char*		cstrData;
//		std::string		strData;
//		::Object*		objData;
//		::Function*		funcData;
		void*			ptrData;
	};

	ValueType type;
	ValueUnion data;

};
//}}}


#endif // VALUE_H
